package query_handler

import (
	"errors"
	"file-cleaner/internal/logger"
	"file-cleaner/internal/pkg/file_cleaner/file_info"
	"file-cleaner/internal/pkg/file_cleaner/pool"
	"file-cleaner/internal/pkg/file_cleaner/queue"
	"fmt"
	"os"
	"sync"
	"time"
)

type QueryHandlerChain struct {
	handlers      []QueryHandler
	filepathQueue queue.Queue
	queryQueue    queue.Queue
	pool          *pool.Pool
	locker        *sync.RWMutex
}

func NewQueryHandlerChain(filepathQueue queue.Queue, queryQueue queue.Queue, pool *pool.Pool) *QueryHandlerChain {
	return &QueryHandlerChain{
		handlers:      make([]QueryHandler, 0),
		filepathQueue: filepathQueue,
		queryQueue:    queryQueue,
		pool:          pool,
		locker:        new(sync.RWMutex),
	}
}

func (c *QueryHandlerChain) RegisterHandler(handlers ...QueryHandler) error {
	if handlers == nil || len(handlers) == 0 {
		return errors.New("can not register with null")
	}

	for i, handler := range handlers {
		err := c.registerHandler(handler)
		if err != nil {
			return fmt.Errorf("failed to register handler which index is %d: %v", i, err)
		}
	}
	return nil
}

func (c *QueryHandlerChain) registerHandler(handler QueryHandler) error {
	if handler == nil {
		return errors.New("can not register the nil handler in QueryHandlerChain")
	}
	c.locker.Lock()
	defer c.locker.Unlock()
	c.handlers = append(c.handlers, handler)
	return nil
}

func (c *QueryHandlerChain) Handle() {
	for {
		n := c.filepathQueue.Size()
		for i := 0; i < n; i++ {
			if c.filepathQueue.IsEmpty() {
				break
			}
			item := c.filepathQueue.Poll()
			if item == queue.Done {
				c.pool.Wait()
				c.queryQueue.Add(item)
				return
			}
			path, ok := item.(string)
			if ok {

				osFileInfo, err := os.Stat(path)
				if err != nil {
					logger.Errorf("Failed to catch file info: %v", err)
					continue
				}
				info := file_info.NewFileInfo(osFileInfo, path, "")

				err = c.pool.Submit(func() {
					c.locker.RLock()
					defer c.locker.RUnlock()

					for idx, handler := range c.handlers {
						if !handler.doHandle(info) {
							logger.Debugf("Failed to query at %d handler", idx)
							return
						}
					}

					c.queryQueue.Add(info)
				})
				if err != nil {
					logger.Errorf("Failed to submit query handler chain task, cased by: %v", err)
				}

			} else {
				logger.Errorf("info(%v) type(%T) is not 'string'", item, item)
			}
		}
		time.Sleep(time.Second)
	}
}
